package com.fzk.blog.settings.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fzk.blog.bean.Role;
import com.fzk.blog.bean.User;
import com.fzk.blog.bean.vo.UserInfo;
import com.fzk.blog.blog.service.IRedisService;
import com.fzk.blog.config.MyMailSender;
import com.fzk.blog.exception.MyException;
import com.fzk.blog.mapper.BlogMapper;
import com.fzk.blog.mapper.CommentMapper;
import com.fzk.blog.mapper.RoleMapper;
import com.fzk.blog.mapper.UserMapper;
import com.fzk.blog.settings.service.IUserService;
import com.fzk.blog.utils.MyAES;
import com.fzk.blog.utils.MyJsonUtil;
import com.fzk.blog.utils.ResponseResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author fzk
 * @date 2021-08-15 16:41
 */
@Service
public class UserService implements IUserService {
    @Resource
    private MyAES myAES;
    @Resource
    private UserMapper userMapper;
    @Resource
    private RoleMapper roleMapper;
    @Resource
    private BlogMapper blogMapper;
    @Resource
    private CommentMapper commentMapper;
    @Resource
    private IRedisService redisService;
    @Resource
    private MyMailSender myMailSender;

    @Override
    public Map<String, Object> doLogin(String phone, String password) throws JsonProcessingException {
        // 1.密码加密
        password = myAES.encryptHex(password);
        // 2.根据phone和密码进行查询
        Map<String, Object> map = new HashMap<>();
        map.put("phone", phone);
        map.put("password", password);
        List<User> users = userMapper.selectByMap(map);
        if (users.size() == 0)
            return ResponseResult.error(400, "账号或密码错误");

        if (users.size() != 1)
            throw new MyException(500, "存在多个匹配账号？？？");
        // 3.登录操作：生成token和获取userInfo
        return doLogin(users.get(0).getId());
    }

    @Override
    public UserInfo getMyInfo(int loginId) throws JsonProcessingException {
        UserInfo userInfo = null;
        // 1.先去redis查
        String json = redisService.get("userInfo:id:" + loginId);
        // 2.没有去数据库查，并放入redis
        if (json != null)
            userInfo = MyJsonUtil.toBean(json, UserInfo.class);
        else {
            // 2.1.查info
            userInfo = userMapper.getUserInfoById(loginId);
            // 2.2.查博客数量
            int blogNumber = blogMapper.getNumberByUserId(loginId, true);
            // 2.3.查评论数量
            int commentNumber = commentMapper.getNumberByUserId(loginId);
            userInfo.setBlogNumber(blogNumber);
            userInfo.setCommentNumber(commentNumber);
            // 2.4 放入redis
            redisService.setEx("userInfo:id:" + loginId, MyJsonUtil.writeToString(userInfo));
        }
        return userInfo;
    }

    @Override
    @Transactional// 开启事务
    public Map<String, Object> doRegister(String phone, String email, String password, String code, int roleId) throws JsonProcessingException {
        // 1.邮箱验证码检验
        String code_sent = redisService.get("email:" + email + "_code");
        if (code_sent == null)
            return ResponseResult.error(400, "验证码已经过期，请重新发送");
        if (!code_sent.equals(code))
            return ResponseResult.error(400, "验证码错误");
        redisService.delete("email:" + email + "_code");//移除redis的code
        // 2.密码加密
        password = myAES.encryptHex(password);

        // 3.判断角色id
        Role role = roleMapper.selectById(roleId);
        if (role == null)
            return ResponseResult.error(400, "角色id不存在：" + roleId);
        if (role.getName().equals("root"))
            return ResponseResult.error(400, "请求权限过大");

        // 4.检查手机号是否已经注册
        Map<String, Object> map = new HashMap<>();
        map.put("phone", phone);
        List<User> users = userMapper.selectByMap(map);
        if (users.size() != 0)
            return ResponseResult.error(400, "手机号：" + phone + " 已经注册");

        // 5.检验邮箱是否注册
        map.clear();
        map.put("email", email);
        users = userMapper.selectByMap(map);
        if (users.size() != 0)
            return ResponseResult.error(400, "邮箱已经被注册：" + email);

        // 6.准备数据
        User user = new User();
        user.setPhone(phone);
        user.setUsername(phone);//默认手机号为用户名
        user.setPassword(password);
        user.setRoleId(roleId);
        user.setHeadPortraitUrl("https://picsum.photos/id/130/500/500");//默认头像
        user.setEmail(email);
        user.setEmailFlag(true);
        // 7.插入数据
        int insert = userMapper.insert(user);
        if (insert != 1)
            throw new MyException(500, "插入失败");
        // 8.登录操作
        return doLogin(user.getId());
    }

    @Override
    @Transactional//开启事务
    public Map<String, Object> updateMyInfo(int loginId, User user) {
        // 1.其他字段的赋值
        user.setEditBy(loginId);
        // 修改的是当前账户的信息
        int i = userMapper.updateById(user);
        if (i != 1)
            throw new MyException(500, "更新失败，id：" + loginId);

        // 3.返回userInfo
        UserInfo userInfo = userMapper.getUserInfoById(user.getId());

        // 4. 直接删除redis的userInfo
        redisService.delete("userInfo:id:" + loginId);
        return ResponseResult.success("修改成功", userInfo);
    }

    @Override
    public List<UserInfo> getUserInfoList() throws JsonProcessingException {
        List<UserInfo> userInfoList = null;
        userInfoList = userMapper.getUserInfoList();
        for (UserInfo userInfo : userInfoList) {
            // 2.查博客数量
            int blogNumber = blogMapper.getNumberByUserId(userInfo.getId(), true);

            // 3.查评论数量
            int commentNumber = commentMapper.getNumberByUserId(userInfo.getId());

            userInfo.setBlogNumber(blogNumber);
            userInfo.setCommentNumber(commentNumber);
        }

        return userInfoList;
    }

    @Override
    public UserInfo getUserInfoById(int id) throws JsonProcessingException {
        UserInfo userInfo = null;
        // 1.先去redis里拿
        String json = redisService.get("userInfo:id:" + id);
        // 2,没有再去数据库查，并放入redis
        if (json != null)
            userInfo = MyJsonUtil.toBean(json, UserInfo.class);
        else {
            // 2.1 查userInfo
            userInfo = userMapper.getUserInfoById(id);
            if (userInfo == null)
                throw new MyException(400, "参数错误，不存在id：" + id);
            // 2.2查博客数量
            int blogNumber = blogMapper.getNumberByUserId(id, true);
            // 2.3.查评论数量
            int commentNumber = commentMapper.getNumberByUserId(id);
            userInfo.setBlogNumber(blogNumber);
            userInfo.setCommentNumber(commentNumber);
            // 2.4.放入redis
            redisService.setEx("userInfo:id:" + id, MyJsonUtil.writeToString(userInfo));
        }

        return userInfo;
    }

    @Override
    public Map<String, Object> getUserInfoListPage(String phone, String username, int blogNumberMin, int blogNumberMax, int index, int pageSize) {
        Map<String, Object> returnMap = new HashMap<>();
        // 1.查 userInfoList
        List<UserInfo> userInfoList = userMapper.getUserInfoListPage(phone, username, blogNumberMin, blogNumberMax, index, pageSize);
        for (UserInfo userInfo : userInfoList) {
            // 2.查博客数量
            //int blogNumber=blogMapper.getNumberByUserId(id,true);

            // 3.查评论数量
            int commentNumber = commentMapper.getNumberByUserId(userInfo.getId());
            //userInfo.setBlogNumber(blogNumber);
            userInfo.setCommentNumber(commentNumber);
        }
        // 2.查total
        List<UserInfo> userInfoListTotal = userMapper.getUserInfoListTotal(phone, username, blogNumberMin, blogNumberMax);
        returnMap.put("data", userInfoList);
        returnMap.put("total", userInfoListTotal.size());
        return returnMap;
    }

    @Override
    @Transactional//开事务
    public Map<String, Object> insertUser(User user, String password, int loginId) throws JsonProcessingException {
        // 1.完善插入数据
        user.setPassword(myAES.encryptHex(password));
        user.setCreateBy(loginId);
        user.setEditBy(loginId);
        user.setFlag(0);

        // 2.1 检查是否有相同的phone
        User user1 = userMapper.getUserByPhone(user.getPhone());
        if (user1 != null)
            return ResponseResult.error(400, "已经存在此手机号：" + user.getPhone());
        // 2.2 检查邮箱是否已经注册
        user1 = userMapper.getUserByEmail(user.getEmail());
        if (user1 != null)
            return ResponseResult.error(400, "此邮箱：" + user.getEmail() + " 已经注册");
        // 2.2 检查role_id
        if (roleMapper.getRoleInfoById(user.getRoleId()) == null)
            return ResponseResult.error(400, "参数错误：不存在角色id：" + user.getRoleId());

        // 3.插入
        userMapper.insert(user);

        // 4.返回UserInfo
        UserInfo userInfo = userMapper.getUserInfoById(user.getId());
        userInfo.setCommentNumber(0);
        userInfo.setBlogNumber(0);

        // 5. 放入redis
        redisService.setEx("userInfo:id:" + user.getId(), MyJsonUtil.writeToString(userInfo));
        return ResponseResult.success("添加成功", userInfo);
    }

    @Override
    public UserInfo getUserInfoWithPwd(int userId) {
        // 1.查询info
        UserInfo userInfo = userMapper.getUserInfoWithPwd(userId);
        if (userInfo == null)
            throw new MyException(400, "参数错误：id不存在：" + userId);
        // 2.查博客数量
        int blogNumber = blogMapper.getNumberByUserId(userId, true);

        // 3.查评论数量
        int commentNumber = commentMapper.getNumberByUserId(userId);

        userInfo.setBlogNumber(blogNumber);
        userInfo.setCommentNumber(commentNumber);
        // 4.解密密码
        userInfo.setPassword(myAES.decryptStr(userInfo.getPassword()));
        return userInfo;
    }

    @Override
    @Transactional//开事务
    public UserInfo updateUser(User user, String password, int loginId) {
        // 1,完善数据
        user.setPassword(myAES.encryptHex(password));
        user.setEditBy(loginId);

        // 2. 检查role_id
        Role role = roleMapper.selectById(user.getRoleId());
        if (role == null)
            throw new MyException(400, "参数错误：不存在角色id：" + user.getRoleId());
        if ("root".equals(role.getName()))
            throw new MyException(400, "不能成为root角色，如需权限，请联系管理员");

        // 3.更新
        int i = userMapper.updateById(user);
        if (i != 1)
            throw new MyException(400, "更新失败：id：" + user.getId());

        // 4. redis直接删userInfo
        redisService.delete("userInfo:id:" + user.getId());

        // 5.查询userInfo
        return userMapper.getUserInfoById(user.getId());
    }

    @Override
    @Transactional//开启事务
    public int deleteUsers(int[] ids, int loginId) {
        // 1.参数处理
        List<Integer> idList = new ArrayList<>();
        for (int i : ids) {
            idList.add(i);
        }
        // 2.查询有无root用户
        List<User> users = userMapper.selectBatchIds(idList);
        if (users.size() != ids.length)
            throw new MyException(400, "参数错误，存在已经删除的id");
        for (User user : users) {
            if (user.getRoleId() == 1)
                throw new MyException(400, "不能删除root用户");
        }
        // 3.删除操作
        int i = userMapper.deleteBatchIds(idList);
        if (i != ids.length)
            throw new MyException(500, "删除失败：ids：" + idList.toString());
        // 4.清redis的userInfo
        for (int id : ids)
            redisService.delete("userInfo:id:" + id);
        return i;
    }

    @Override
    @Transactional//开事务
    public void resetPwd(int loginId, String newPassword) {
        // 新密码加密
        newPassword = myAES.encryptHex(newPassword);
        if (1 != userMapper.resetPwd(loginId, newPassword))
            throw new MyException(400, "密码修改失败：新密码不能和旧密码一致");
    }

    @Override
    public Map<String, Object> sendCode(String email) {
        // 1.发验证码
        String code = myMailSender.sendMail(email);
        if (code == null)//说明发送失败
            return ResponseResult.error(400, "邮件发送失败，请检查您的邮箱是否正确，请稍后尝试，或联系管理员");
        // 2.发送成功，放入redis
        redisService.setEx("email:" + email + "_code", code);
        return ResponseResult.success("验证码已发送至您的邮箱：" + email + " 请注意查收，有效期为1h。如果长时间收不到验证码，可以查看垃圾邮箱，可能是被自动放入了垃圾邮件之中。");
    }

    @Override
    public Map<String, Object> sendCode(String phone, String email) {
        // 1.根据phone查user
        User user = userMapper.getUserByPhone(phone);
        if (user == null)
            return ResponseResult.error(400, "该用户未注册或被删除");
        // 2.检查邮箱
        if (!user.getEmailFlag())
            return ResponseResult.error(400, "此账号的邮箱未验证，无法发送验证码，如果需要修改密码，请联系管理员");
        if (!user.getEmail().equals(email))
            return ResponseResult.error(400, "此账号绑定的邮箱与您所填写的邮箱不一致");
        // 3.发送验证码咯
        String code = myMailSender.sendMail(email);
        if (code == null)//说明发送失败
            return ResponseResult.error(400, "邮件发送失败，请检查您的邮箱是否正确，请稍后尝试，或联系管理员");
        // 4.发送成功，放入redis
        redisService.setEx("email:" + email + "_code", code);
        return ResponseResult.success("验证码已经发送至您的邮箱：" + email + "，请注意查收，有效期为1h。如果长时间收不到验证码，可以查看垃圾邮箱，可能是被自动放入了垃圾邮件之中。");
    }

    @Override
    public Map<String, Object> doForgetPwd(String phone, String email, String password, String code) throws JsonProcessingException {
        // 1.根据phone查user
        User user = userMapper.getUserByPhone(phone);
        if (user == null)
            return ResponseResult.error(400, "该用户未注册或被删除");
        // 2,检查邮箱
        if (!user.getEmailFlag())
            return ResponseResult.error(400, "此账号的邮箱未验证，无法发送验证码，如果需要修改密码，请联系管理员");
        if (!user.getEmail().equals(email))
            return ResponseResult.error(400, "此账号绑定的邮箱与您所填写的邮箱不一致");
        // 3.去redis检查验证码咯
        String code_sent = redisService.get("email:" + email + "_code");
        if (code_sent == null)
            return ResponseResult.error(400, "验证码已过期或还未发送验证码，请重新发送验证码");
        // 4.验证码没问题，可以修改密码啦
        if (!code.equals(code_sent))
            return ResponseResult.error(400, "验证码错误");

        // 5.开始修改密码
        resetPwd(user.getId(), password);
        // 6.做登录操作
        return doLogin(user.getId());
    }

    /**
     * 登录操作，将会生成token，并查询userInfo返回
     *
     * @param loginId 登录id
     * @return 返回token的userInfo
     * @throws JsonProcessingException
     */
    private Map<String, Object> doLogin(int loginId) throws JsonProcessingException {
        // 1.先登录呢，生成token
        StpUtil.login(loginId);

        // 2.获取token
        String tokenName = StpUtil.getTokenName();
        String tokenValue = StpUtil.getTokenValue();

        // 3.获取userInfo
        UserInfo userInfo = getMyInfo(loginId);

        // 4.返回数据
        Map<String, Object> map = ResponseResult.success("登录成功", userInfo);
        map.put(tokenName, tokenValue);
        return map;
    }
}
